/* COPYRIGHT_CHUNFENG */
#include "cf_std.h"
#include "cf_hash_table.h"
#include "lcf_log.h"

int CFHashTableNodeInit(CFHashTableNode* node, void* ptr)
{
    memset(node, 0, sizeof(CFHashTableNode));
    node->ptr = ptr;
    cf_list_init(&node->list);
    return 0;
}

CFHashTableNode* CFHashTableNodeNew(void* ptr)
{
    CFHashTableNode* ret;

    ret = (CFHashTableNode*)malloc(sizeof(CFHashTableNode));
    if(!ret){
        return NULL;
    }
    ret->ptr = ptr;
    cf_list_init(&ret->list);
    return ret;
}

int CFHashTableNodeFree(CFHashTableNode* node)
{
    free(node);
    return 0;
}

CFHashTableNode* CFHashTableNodeNext(CFHashTableNode* node)
{
    return CFListContainerNext(node, CFHashTableNode, list);
}

CFHashTableNode* CFHashTableNodesGet(CFHashTableNode* head, void* key, BOOL(*ptrKeyMatchFn)(void* ptr, void* key))
{
    CFListContainerForeach(head, node, CFHashTableNode, list){
        if(ptrKeyMatchFn(node->ptr, key)){
            return node;
        }
    }
    return NULL;
}

void* CFHashTableNodesGetPtrByKey(CFHashTableNode* head, void* key, BOOL(*ptrKeyMatchFn)(void* ptr, void* key))
{
    CFListContainerForeach(head, node, CFHashTableNode, list){
        if(ptrKeyMatchFn(node->ptr, key)){
            return node->ptr;
        }
    }
    return NULL;
}

int CFHashTableNodesAddPtr(CFHashTableNode* head, void* ptr, void* key, BOOL(*ptrKeyMatchFn)(void* ptr, void* key))
{
    CFHashTableNode *newNode;

    if(CFHashTableNodesGetPtrByKey(head, key, ptrKeyMatchFn)){
            LCF_ERR("Node already exist!!");
            return -1;
    }
    newNode = CFHashTableNodeNew(ptr);
    if(!newNode){
        return -1;
    }
    cf_list_insert_pre(&head->list, &newNode->list);
    return 0;
}

int CFHashTableNodesDel(CFHashTableNode* node)
{
    cf_list_del(&node->list);
    return 0;
}

int CFHashTableNodesDelFree(CFHashTableNode* node)
{
    CFHashTableNodesDel(node);
    CFHashTableNodeFree(node);
    return 0;
}

int CFHashTableNodesFreeAll(CFHashTableNode* head)
{
    CFListContainerForeach(head, node, CFHashTableNode, list){
        CFHashTableNodeFree(node);
    }
    return CFHashTableNodeInit(head, NULL);
}

int CFHashTableInit(CFHashTable* tbl, int size, int(*hashFn)(void* ptr), BOOL(*ptrKeyMatchFn)(void* ptr, void* key))
{
    int i;

    memset(tbl, 0, sizeof(CFHashTable));
    tbl->size = size;
    tbl->nodeArray = malloc(sizeof(CFHashTableNode) * tbl->size);
    if(!tbl->nodeArray){
        goto ERR_OUT;
    }
    memset(tbl->nodeArray, 0, sizeof(CFHashTableNode) * tbl->size);
    for(i=0; i< tbl->size; i++){
        cf_list_init(&tbl->nodeArray[i].list);
    }
    tbl->hashFn = hashFn;
    tbl->ptrKeyMatchFn = ptrKeyMatchFn;
    return 0;
ERR_OUT:
    return -1;
}

int CFHashTableExit(CFHashTable* tbl)
{
    int i;

    if(tbl->nodeArray){
        for(i=0; i<tbl->size; i++){
                CFHashTableNodesFreeAll(&tbl->nodeArray[i]);
        }
        free(tbl->nodeArray);
    }
    return 0;
}

CFHashTable* CFHashTableNew(int size, int(*hashFn)(void* key), void*(*ptrToKeyFn)(void* ptr), BOOL(*ptrKeyMatchFn)(void* ptr, void* key))
{
    CFHashTable* ret;

    ret = malloc(sizeof(CFHashTable));
    if(!ret){
        goto ERR_OUT;
    }
    if(CFHashTableInit(ret, size, hashFn, ptrToKeyFn, ptrKeyMatchFn)){
        LCF_ERR("\n");
        goto ERR_FREE_TBL;
    }
    return ret;
ERR_FREE_TBL:
    free(ret);
ERR_OUT:
    return NULL;
}

void* CFHashTableGet(CFHashTable* tbl, void* key)
{
    void* ret;
    int i;

    for(i=0; i<tbl->size; i++){
        ret = CFHashTableNodesGetPtrByKey(&tbl->nodeArray[i], key, tbl->ptrKeyMatchFn);
        if(ret)
            return ret;
    }
    return NULL;
}

int CFHashTableFree(CFHashTable* tbl)
{
    if(CFHashTableExit(tbl))
        return -1;
    free(tbl);
    return 0;
}

int CFHashTableAdd(CFHashTable* this, void* ptr)
{
    int idx;
    CFHashTableNode *newNode;

    newNode = CFHashTableNodeNew(ptr);
    idx = this->hashFn(this->ptrToKeyFn(ptr));
    return CFHashTableNodesAddPtr(&this->nodeArray[idx], ptr, this->ptrToKeyFn(ptr), this->ptrKeyMatchFn);
}

int CFHashTableDel(CFHashTable* this, void* key)
{
    int idx;
    CFHashTableNode *node;

    idx = this->hashFn(key);
    node = CFHashTableNodesGet(&this->nodeArray[idx], key, this->ptrKeyMatchFn);
    if(!node){
        return -1;
    }
    return CFHashTableNodesDelFree(node);
}
